﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace Kernel
{
    public enum MessageType
    {
        tok,
        wakeup,
        one,
        two,
        small
    }

    public struct Msg
    {
        // отличительный признак 
        public int number;
        public MessageType mesType;
        // номер отправителя
        public int sender_id;

        public Msg(int num, MessageType mes)
        {
            number = num;
            mesType = mes;
            sender_id = -1;
        }

        public Msg(int num, int sender,MessageType mes)
        {
            number = num;
            mesType = mes;
            sender_id = sender;
        }
    }

    public class GraphNode
    {
        public enum NodeState
        {
            Leader,
            Lost,
            Sleep,
            Cand,
            Active,
            Passive
        }

        private readonly List<GraphNode> _children = new List<GraphNode>();
        
        public List<string> Tag { get; private set; }
        public string Name { get; private set; }
        public bool IsInitiator { set; get; }
        private NodeState _state;
        public NodeState State
        {
            get
            {
                return _state;
            }
            set
            {
                if(rtbLog != null)
                    rtbLog.Text += String.Format("Процес {0} изменил состояние на: {1} \n", this.Number, 
                        Enum.GetName(typeof(NodeState), value));
                _state = value;
            }
        }

        public GraphNode Parent { get; set; }
        // подсчет числа сообщений, полученных от входящих соседей
        public Dictionary<string, int> Rec { get; private set; }
        public Dictionary<string, int> Snd { get; private set; }
        public List<string> Inc { get; private set; }
        public List<string> NInc { get; private set; }

        //public Dictionary<MessageType, int> MessagesBuffer { get; private set; }
        public RichTextBox rtbLog;
        public Queue<Msg> MessagesBuffer { get; private set; }
        public int Number { get; private set; }
        public Queue<Msg> ReceivedMessages { get; private set; }
        public Queue<Msg> ShippedMessages { get; private set; }
        public string CurrentMessage
        {
            get
            {
                Msg tmp;
                if (ShippedMessages != null && ShippedMessages.Count != 0)
                {
                    tmp = ShippedMessages.Peek();
                    return String.Format("<{0}, {1}>", tmp.mesType.ToString(), tmp.number);
                }
                else
                    return "<>";
            }
        }
        //данные для алгоритма Петерсона
        /// <summary>
        /// -1 - не известно победил или нет, иначе содержится признак победителя
        /// </summary>
        public int Win { get; set; }
        /// <summary>
        /// undef это значение -1.
        /// </summary>
        public int Acn { get; set; }
        /// <summary>
        /// undef это значение -1.
        /// </summary>
        public int Ci { get; set; }

        //помещаем сюда внутреннее состояние узла для отображения на форме в виде всплывающей подсказки
        public string ToolTipMessage { get; set; }

        public List<GraphNode> Children
        {
            get { return _children; }
        }

        public GraphNode(string name)
        {
            State = NodeState.Sleep;
            Name = name;
            IsInitiator = false;
            Rec = new Dictionary<string, int>();
            Tag = new List<string>();
            Inc = new List<string> {Name};
            NInc = new List<string>();
            Snd= new Dictionary<string, int>();

            //MessagesBuffer = new Dictionary<MessageType, int>();
            MessagesBuffer = new Queue<Msg>();
            Number = Convert.ToInt32(name);
            ReceivedMessages = new Queue<Msg>();
            ShippedMessages = new Queue<Msg>();
        }

        public void AddChild(GraphNode child)
        {
            _children.Add(child);
            child.Rec.Add(Name, 0);
            Snd.Add(child.Name, 0);
        }

        public void AddChildren(List<GraphNode> children)
        {
            children.ForEach(AddChild);
        }

        public void Send(GraphNode node)
        {
            node.Rec[Name]++;
        }

        public void SendMessage(GraphNode node, Msg mes)
        {
            this.ShippedMessages.Enqueue(mes);
            //if (mes.mesType != MessageType.wakeup)
            //    node.Rec[Name]++;
            Snd[node.Name]++;
            node.MessagesBuffer.Enqueue(mes);
            rtbLog.Text += String.Format("Процесс {0} отправил процессу {1} сообщение <{2}, {3}> \n",
               this.Number, node.Number, mes.mesType.ToString(), mes.number);
        }

        /// <summary>
        /// "принимает" все сообщения из очереди MessagesBuffer, в очередь ReceivedMessages
        /// </summary>
        public void ReceiveMessages()
        {
            //Не должно оставаться паразитных сообщений с "предыдущего" шага. 
            //Которые отослали (или не отослали), но в очереди лежат
            ReceivedMessages.Clear();

            while(MessagesBuffer.Count != 0)
            {
                ReceivedMessages.Enqueue(MessagesBuffer.Dequeue());
            }

            this.ShippedMessages.Clear();
        }

        // Взять последнее входящее сообщение
        public Msg GetMessage()
        {
            var msg = ReceivedMessages.Dequeue();
            rtbLog.Text += String.Format("Процесс {0} получил от процесса {1} сообщение <{2}, {3}> \n",
              this.Number, msg.sender_id, msg.mesType.ToString(), msg.number);
            return msg;
        }

        // используется в фазовом алгоритме
        public int GetMinRec()
        {
            int res = int.MaxValue;
            foreach (var r in Rec.Values)
                if (r < res)
                    res = r;

            return res;
        }
    }
}
